package ufabc.edu.va.agenteminhoca;

import java.util.LinkedList;

import sim.engine.SimState;
import sim.engine.Steppable;
import sim.engine.Stoppable;
import sim.util.Int2D;

@SuppressWarnings("serial")
public class Mundo implements Steppable {
	private Minhocas minhocaState;
	private Minhoca[] minhocas;
	private Comida[] comidas;
	private int highscore;
    

	public int getHighscore() {
		return highscore;
	}


	public Mundo(Minhocas state) {
		this.minhocaState = state;
		this.minhocas = new Minhoca[minhocaState.getPopulationSize()];
		this.comidas = new Comida[minhocaState.getPlantCount()];
		
		for (int i = 0; i < minhocaState.getPopulationSize(); i++) {
			minhocas[i] = new Minhoca();
		}
		for (int i = 0; i < minhocaState.getPlantCount(); i++) {
			comidas[i] = new Comida();			
		}
		
		resetWorld();
	}
			
	
	@Override
	public void step(SimState state) {
		if (minhocaState.schedule.getSteps() % minhocaState.getYearDuration() == 0 && minhocaState.schedule.getSteps() > 0) {
			for (int i = 0; i < minhocas.length; i++) {
				minhocas[i].stop();
			}
			
			if (minhocaState.getInfo() != null) {
				int ano = (int) (minhocaState.schedule.getSteps() / minhocaState.getYearDuration());
				
				minhocaState.getInfo().addLine(ano, getHighScore(), getPontuacaoMedia(), getPontuacaoTotal());
			}
			breed();
			resetWorld();
		}
	}
	

	public void resetWorld() {
		minhocaState.worldMinhocas.clear();
		minhocaState.worldComidas.clear();
		
		for(int i = 0; i < minhocas.length; i++)
		{
			Int2D location = new Int2D(minhocaState.random.nextInt(minhocaState.getGridWidth()),
					minhocaState.random.nextInt(minhocaState.getGridHeight()));
			
			minhocaState.worldMinhocas.setObjectLocation(minhocas[i], location);
			
			Stoppable stoppable = minhocaState.schedule.scheduleRepeating(minhocas[i]);
			minhocas[i].stoppable = stoppable;
		}
		
		for(int i = 0; i < minhocaState.getPlantCount(); i++)
		{
			Int2D location = new Int2D(minhocaState.random.nextInt(minhocaState.getGridWidth()), 
					minhocaState.random.nextInt(minhocaState.getGridHeight()));
			minhocaState.worldComidas.setObjectLocation(comidas[i], location);
		}
	}
	
	private int getHighScore() {
		int highScore = 0;
		for (int i = 0; i < minhocas.length; i++) {
			if (highScore < minhocas[i].getScore()) {
				highScore = minhocas[i].getScore();
			}
		}
		
		return highScore;
	}
	
	private double getPontuacaoMedia() {
		int pontuacaoTotal = getPontuacaoTotal();
		
		return pontuacaoTotal / minhocaState.getPopulationSize();
	}


	private int getPontuacaoTotal() {
		int pontuacaoTotal = 0;
		for (int i = 0; i < minhocas.length; i++) {
			pontuacaoTotal += minhocas[i].getScore();
		}
		return pontuacaoTotal;
	}

	private void breed() {
		int twicePopulation = minhocaState.getPopulationSize() * 2;
		LinkedList<Minhoca> cruzandoMinhocas = new LinkedList<Minhoca>();
		this.highscore = getHighScore();
		double adaptabilidade = 0;
		
		double pontuacaoMedia = getPontuacaoMedia();
		int contadorDeMinhocas = 0;
		do {
			for (int i = 0; i < minhocas.length; i++) {
				if(pontuacaoMedia != 0)
					adaptabilidade = minhocas[i].getScore() / pontuacaoMedia;
				
				while ((adaptabilidade >= 1) && (contadorDeMinhocas < twicePopulation)) {
					adaptabilidade = adaptabilidade - 1;
					cruzandoMinhocas.add(new Minhoca(minhocas[i].gene));
					contadorDeMinhocas = contadorDeMinhocas + 1;
				}
				if (Math.random() < adaptabilidade  && contadorDeMinhocas < twicePopulation) {
					cruzandoMinhocas.add(new Minhoca(minhocas[i].gene));
					contadorDeMinhocas = contadorDeMinhocas + 1;
				}
			}
		} while (contadorDeMinhocas == 0);

		double mutationProbability = minhocaState.getMutationProbability();
		if (mutationProbability > 0) {
			for (Minhoca minhoca : cruzandoMinhocas) {
				minhoca.mutate(mutationProbability);
			}
		}
		
		if (mutationProbability > 0) {
			crossOver(cruzandoMinhocas, contadorDeMinhocas);
		}
		
		if (minhocaState.isFixedPopulation()) {
			for (int i = 0; i < minhocas.length; i++) {
				if (i < cruzandoMinhocas.size()) {
					minhocas[i] = cruzandoMinhocas.get(i);
				} else {
					minhocas[i] = new Minhoca();
				}
			}
		} else {
			minhocas = cruzandoMinhocas.toArray(new Minhoca[contadorDeMinhocas]);
		}
	}
	
	private void crossOver(LinkedList<Minhoca> cruzandoMinhocas, int contadorDeMinhocas) {
		int[] choose = new int[contadorDeMinhocas];
		for (int i = 0; i < contadorDeMinhocas; i++)
			choose[i] = i;
		int stop = contadorDeMinhocas - (int)(contadorDeMinhocas * minhocaState.getCrossoverProbability());
		if (stop < 0)
			stop = 0;
		while (contadorDeMinhocas > stop) {
			int r = (int)(Math.random() * contadorDeMinhocas);
			int a = choose[r];
			choose[r] = choose[contadorDeMinhocas - 1];
			contadorDeMinhocas = contadorDeMinhocas - 1;
			r = (int)(Math.random()*contadorDeMinhocas);
			int b = choose[r];
			choose[r] = choose[contadorDeMinhocas];
			contadorDeMinhocas = contadorDeMinhocas - 1;
			cruzandoMinhocas.get(a).gene.crossWith(cruzandoMinhocas.get(b).gene);
		}
	}


}
